package mylab.io.core.codec;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.base.Preconditions;

import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.handler.codec.ByteToMessageDecoder;
import mylab.io.base.model.Msg;
import mylab.io.base.property.DasProperties;
import mylab.io.core.context.CoreContext;
import mylab.utils.common.ReflectUtil;

/**
 * 服务于tcp链路
 * <>
 * 基于tcp的stream特性,要考虑分包、粘包现象，需要识别协议帧（frameDecoder）
 * </>
 */
public abstract class AbstractFrameCodec<P extends DasProperties, M extends Msg>
        extends AbstractCodec<P, M, ByteBuf> {

    static final String HANDLER_NAME_FRAME_DECODER = "frameDecoderHandler";

    protected Class<? extends ByteToMessageDecoder> frameDecoderClass;

    public AbstractFrameCodec(CoreContext<P, M> context, MsgResolver<M, ByteBuf> resolver, Class<? extends ByteToMessageDecoder> frameDecoderClass) {
        super(context, resolver);
        this.frameDecoderClass = frameDecoderClass;
    }

    @Override
    public void initPipeLine(Channel channel) throws Exception {
        super.initPipeLine(channel);

        ByteToMessageDecoder frameDecoder = newFrameDecoder();
        Preconditions.checkNotNull(frameDecoder, "frameDecoder is null");

        AbstractInboundHandler inboundHandler = new InboundFrameHandler(getContext());
        ExceptionHandler exceptionHandler = new ExceptionHandler();
        SimpleDecoder<M> simpleDecoder = new SimpleDecoder<>(resolver);
        SimpleEncoder<M> simpleEncoder = new SimpleEncoder<>(resolver);

        //in
        channel.pipeline().addLast(HANDLER_NAME_FRAME_DECODER, frameDecoder);
        channel.pipeline().addLast(SimpleDecoder.HANDLER_NAME, simpleDecoder);
        channel.pipeline().addLast(AbstractInboundHandler.HANDLER_NAME, inboundHandler);

        //out
        channel.pipeline().addLast(SimpleEncoder.HANDLER_NAME, simpleEncoder);

        //exception
        channel.pipeline().addLast(ExceptionHandler.HANDLER_NAME, exceptionHandler);
    }

    /**
     * 协议帧解码器
     * 参见 netty 默认提供的 FrameDecoder
     *
     * @see io.netty.handler.codec.DelimiterBasedFrameDecoder       基于分隔符
     * @see io.netty.handler.codec.FixedLengthFrameDecoder          基于固定消息长度
     * @see io.netty.handler.codec.LengthFieldBasedFrameDecoder     基于消息长度
     * @see io.netty.handler.codec.LineBasedFrameDecoder            基于文本换行
     */
    @JsonIgnore
    protected ByteToMessageDecoder newFrameDecoder() {
        return ReflectUtil.newInstance(frameDecoderClass);
    }

}
